﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using Lwllm.ArcengineProxy.Common;
using Lwllm.ArcengineProxy.Core.Annotations;
using Lwllm.ArcengineProxy.Core.Interfaces;
using Lwllm.ArcengineProxy.Core.Models;

namespace Lwllm.ArcengineProxy.Core.Proxys
{
    /// <summary>
    ///     featureclass的代理实现类
    /// </summary>
    public class FeatureClassProxy : IFeatureClass, IDisposable
    {
        private IFeatureClass _featureClass;

        public IFeatureClass FeatrueClass
        {
            get { return _featureClass; }
        }

        public string FeatureClassProxyId { get; }

        private Dictionary<string, int> _colMappings = new Dictionary<string, int>();

        #region featureclass成员

        public FeatureClassProxy(IFeatureClass featureClass)
        {
            _featureClass = featureClass;
            FeatureClassProxyId = Guid.NewGuid().ToString();
        }

        public void Dispose()
        {
            _featureClass?.ComDispose();
            _featureClass = null;
        }

        int IClass.FindField(string name)
        {
            return _featureClass.FindField(name);
        }

        void IFeatureClass.AddField(IField field)
        {
            _featureClass.AddField(field);
        }

        void IFeatureClass.DeleteField(IField field)
        {
            _featureClass.DeleteField(field);
        }

        void IFeatureClass.AddIndex(IIndex index)
        {
            _featureClass.AddIndex(index);
        }

        void IFeatureClass.DeleteIndex(IIndex index)
        {
            _featureClass.DeleteIndex(index);
        }

        IFields IFeatureClass.Fields => _featureClass.Fields;

        IIndexes IFeatureClass.Indexes => _featureClass.Indexes;

        bool IFeatureClass.HasOID => _featureClass.HasOID;

        string IFeatureClass.OIDFieldName => _featureClass.OIDFieldName;

        UID IFeatureClass.CLSID => _featureClass.CLSID;

        UID IFeatureClass.EXTCLSID => _featureClass.EXTCLSID;

        object IFeatureClass.Extension => _featureClass.Extension;

        IPropertySet IFeatureClass.ExtensionProperties => _featureClass.ExtensionProperties;

        int IFeatureClass.ObjectClassID => _featureClass.ObjectClassID;

        public IFeature CreateFeature()
        {
            return _featureClass.CreateFeature();
        }

        public IFeature GetFeature(int id)
        {
            return _featureClass.GetFeature(id);
        }

        public IFeatureCursor GetFeatures(object fids, bool recycling)
        {
            return _featureClass.GetFeatures(fids, recycling);
        }

        public IFeatureBuffer CreateFeatureBuffer()
        {
            return _featureClass.CreateFeatureBuffer();
        }

        public int FeatureCount(IQueryFilter queryFilter)
        {
            return _featureClass.FeatureCount(queryFilter);
        }

        public IFeatureCursor Search(IQueryFilter filter, bool recycling)
        {
            return _featureClass.Search(filter, recycling);
        }

        public IFeatureCursor Update(IQueryFilter filter, bool recycling)
        {
            return _featureClass.Update(filter, recycling);
        }

        public IFeatureCursor Insert(bool useBuffering)
        {
            return _featureClass.Insert(useBuffering);
        }

        public ISelectionSet Select(IQueryFilter queryFilter, esriSelectionType selType, esriSelectionOption selOption,
            IWorkspace selectionContainer)
        {
            return _featureClass.Select(queryFilter, selType, selOption, selectionContainer);
        }

        int IFeatureClass.FindField(string name)
        {
            return _featureClass.FindField(name);
        }

        void IObjectClass.AddField(IField field)
        {
            _featureClass.AddField(field);
        }

        void IObjectClass.DeleteField(IField field)
        {
            _featureClass.DeleteField(field);
        }

        void IObjectClass.AddIndex(IIndex index)
        {
            _featureClass.AddIndex(index);
        }

        void IObjectClass.DeleteIndex(IIndex index)
        {
            _featureClass.DeleteIndex(index);
        }

        IFields IObjectClass.Fields => _featureClass.Fields;

        IIndexes IObjectClass.Indexes => _featureClass.Indexes;

        bool IObjectClass.HasOID => _featureClass.HasOID;

        string IObjectClass.OIDFieldName => _featureClass.OIDFieldName;

        UID IObjectClass.CLSID => _featureClass.CLSID;

        UID IObjectClass.EXTCLSID => _featureClass.EXTCLSID;

        object IObjectClass.Extension => _featureClass.Extension;

        IPropertySet IObjectClass.ExtensionProperties => _featureClass.ExtensionProperties;

        int IObjectClass.ObjectClassID => _featureClass.ObjectClassID;

        IEnumRelationshipClass IObjectClass.get_RelationshipClasses(esriRelRole role)
        {
            return _featureClass.RelationshipClasses[role];
        }

        string IFeatureClass.AliasName => _featureClass.AliasName;

        public esriGeometryType ShapeType => _featureClass.ShapeType;
        public esriFeatureType FeatureType => _featureClass.FeatureType;
        public string ShapeFieldName => _featureClass.ShapeFieldName;
        public IField AreaField => _featureClass.AreaField;
        public IField LengthField => _featureClass.LengthField;
        public IFeatureDataset FeatureDataset => _featureClass.FeatureDataset;
        public int FeatureClassID => _featureClass.FeatureClassID;

        IEnumRelationshipClass IFeatureClass.get_RelationshipClasses(esriRelRole role)
        {
            return _featureClass.RelationshipClasses[role];
        }

        string IObjectClass.AliasName => _featureClass.AliasName;

        int IObjectClass.FindField(string name)
        {
            return _featureClass.FindField(name);
        }

        void IClass.AddField(IField field)
        {
            _featureClass.AddField(field);
        }

        void IClass.DeleteField(IField field)
        {
            _featureClass.DeleteField(field);
        }

        void IClass.AddIndex(IIndex index)
        {
            _featureClass.AddIndex(index);
        }

        void IClass.DeleteIndex(IIndex index)
        {
            _featureClass.DeleteIndex(index);
        }

        IFields IClass.Fields => _featureClass.Fields;

        IIndexes IClass.Indexes => _featureClass.Indexes;

        bool IClass.HasOID => _featureClass.HasOID;

        string IClass.OIDFieldName => _featureClass.OIDFieldName;

        UID IClass.CLSID => _featureClass.CLSID;

        UID IClass.EXTCLSID => _featureClass.EXTCLSID;

        object IClass.Extension => _featureClass.Extension;

        IPropertySet IClass.ExtensionProperties => _featureClass.ExtensionProperties;

        #endregion

        #region  扩展方法

        public int FindField(string fieldName)
        {
            return _featureClass.FindField(fieldName);
        }

        public List<TEntity> ToList<TEntity>() where TEntity : BaseEntity
        {
            var list = new List<TEntity>();
            var properties = GetMembers<TEntity>();
            IFeatureCursor cursor = _featureClass.Search(null, false);
            IFeature feature;
            while ((feature = cursor.NextFeature()) != null)
            {
                var item = (TEntity)Activator.CreateInstance(typeof(TEntity), feature, this);
                foreach (var property in properties)
                {
                    SetValue(feature, property, item);
                }

                list.Add(item);
            }

            return list;
        }

        private List<PropertyInfo> GetMembers<TEntity>() where TEntity : BaseEntity
        {
            var propertyInfos = typeof(TEntity).GetProperties()
                .Where(p => !p.GetCustomAttributes<ArcengineMemberAttribute>(false).Any()
                            && !p.GetCustomAttributes<UnMappedFieldAttribute>(true).Any());
            return propertyInfos.ToList();
        }

        private void SetValue<TEntity>(IFeature feature, PropertyInfo property, TEntity item) where TEntity : BaseEntity
        {
            var name = property.Name;
            int colIndex;
            if (_colMappings.ContainsKey(name))
            {
                colIndex = _colMappings[name];
            }
            else
            {
                var propertyDescriptor = TypeDescriptor.GetProperties(item)[name];
                var nameAttribute = propertyDescriptor.Attributes[typeof(FieldNameAttribute)];
                if (nameAttribute != null)
                {
                    name = ((FieldNameAttribute)nameAttribute).Name;
                    colIndex = feature.Fields.FindField(name);
                    if (colIndex >= 0)
                    {
                        _colMappings.Add(property.Name, colIndex);
                        propertyDescriptor.SetValue(item, feature.Value[colIndex]);
                    }
                    return;
                }
                colIndex = feature.Fields.FindField(name);
                _colMappings.Add(property.Name, colIndex);
            }

            if (colIndex >= 0)
            {
                var propertyDescriptor = TypeDescriptor.GetProperties(item)[name];
                propertyDescriptor?.SetValue(item, feature.Value[colIndex]);
            }
        }

        #endregion
    }
}